#pragma once

#include "my_defs.h"
#include <stdarg.h>

typedef struct {
    int64_t             len;
    char                *data;
} my_str_t;

#define MY_STRING(str)                  { (int64_t)strlen(str), str }
#define MY_NULL_STRING                  { 0, NULL }
#define MY_STR_SET(dest, str)           (dest)->len = (int64_t)strlen(str); (dest)->data = (str)
#define MY_STR_SET_NULL(dest)           (dest)->len = 0; (dest)->data = NULL

/*
 * the simple inline cycle copies the variable length strings up to 16
 * bytes faster than icc8 autodetecting _intel_fast_memcpy()
 */
static inline char *
my_copy(char *dst, const char *src, int64_t len)
{
    char *td = dst;

    if (len < 17) {
        while(len) {
            *dst++ = *src++;
            len--;
        }
        return td;
    }

    return memcpy(dst, src, (size_t)len);
}

static inline void
my_str_to_string(const my_str_t *str, char *buf, int64_t buf_size)
{
    int64_t len = buf_size - 1;
    if (str->len < len) {
        len = str->len;
    }

    my_copy(buf, str->data, len);
    buf[len] = '\0';
}

static inline int
my_str_cmp(my_str_t *str1, my_str_t *str2)
{
    int64_t n = str1->len < str2->len ? str1->len : str2->len;

    char a, b;
    for (int64_t i = 0; i < n; i++) {
        a = *(str1->data + i);
        b = *(str2->data + i);

        if (a > b) {
            return 1;
        }

        if (a < b) {
            return -1;
        }
    }

    if (str1->len == str2->len) {
        return 0;
    }

    return str1->len > str2->len ? 1 : -1;
}

static inline int
my_str_case_cmp(my_str_t *str1, my_str_t *str2)
{
    int64_t n = str1->len < str2->len ? str1->len : str2->len;

    char a, b;
    for (int64_t i = 0; i < n; i++) {
        a = (char)tolower(*(str1->data + i));
        b = (char)tolower(*(str2->data + i));

        if (a > b) {
            return 1;
        }

        if (a < b) {
            return -1;
        }
    }

    if (str1->len == str2->len) {
        return 0;
    }

    return str1->len > str2->len ? 1 : -1;
}

static inline my_bool_t
my_str_to_int64(my_str_t *str, int64_t *res)
{
    if (str->len == 0) {
        *res = 0;
        return MY_TRUE;
    }

    char    ch;
    int64_t r = 0;
    int     flag = 1;
    char    *p = str->data;
    char    *end = str->data + str->len;

    if (*p == '-') {
        flag = -1;
        p++;
    } else if (*p == '+') {
        p++;
    }

    for (; p != end; p++) {
        ch = *p;
        if (ch < '0' || ch > '9') {
            return MY_FALSE;
        }

        r = r * 10 + (ch - '0');
    }

    *res = r * flag;
    return MY_TRUE;
}

static inline my_bool_t
my_str_to_double(my_str_t *str, double *res)
{
    if (str->len == 0) {
        *res = 0.0;
        return MY_TRUE;
    }

    char    ch;
    double  r       = 0;
    double  flag    = 1;
    double  e       = 0;
    double  eflag   = 1;
    double  deviced = 1;
    char *p = str->data;
    char *end = str->data + str->len;

    if (*p == '-') {
        flag = -1;
        p++;
    } else if (*p == '+') {
        p++;
    }

    for (; p != end; p++) {
        ch = *p;
        if (ch < '0' || ch > '9') {
            break;
        }

        r = r * 10 + (ch - '0');
    }

    if (p != end && *p == '.') {
        p++;
        for (; p != end; p++) {
            ch = *p;
            if (ch < '0' || ch > '9') {
                break;
            }

            r = r * 10 + (ch - '0');
            deviced *= 10;
        }
    }

    if (p != end && *p == 'e') {
        p++;
        switch(*p) {
        case '+':
            p++;
            break;
        case '-':
            eflag = -1;
            p++;
            break;
        default:
            break;
        }

        for (; p != end; p++) {
            ch = *p;
            if (ch < '0' || ch > '9') {
                break;
            }

            e = e * 10 + (ch - '0');
        }
    }

    if (p != end) {
        return MY_FALSE;
    }

    if (eflag > 0) {
        *res = (flag * r) / deviced * pow10(e);
    } else {
        *res = (flag * r) / deviced / pow10(e);
    }

    return MY_TRUE;
}


#define MY_MEMCPY(dst, src, n)   (void) memcpy(dst, src, n)
#define MY_CPYMEM(dst, src, n)   (((char *) memcpy(dst, src, n)) + (n))

static inline int64_t
my_strlen_try(const char *str, int64_t max)
{
    for (int64_t i = 0; i < max; i++) {
        if (*(str+i) == '\0') {
            return i;
        }
    }

    return -1;
}

/*
 * support formats: 
 * %d, %ld                      int/int32/int64
 * %ud, %uld                    uint/uint32/uint64
 * %f, %lf                      float/double, max valid number fits to %18.15f
 * %p                           void *
 * %v                           my_str_t *
 * %c                           char
 * %s                           char *
 */
int my_snprintf(char *buf, size_t max, const char *fmt, ...);
int my_slprintf(char *buf, char *last, const char *fmt, ...);
char *my_vslprintf(char *buf, char *last, const char *fmt, va_list args);

static inline const char *
my_path_base(const char *path)
{
    unsigned last = 0;
    for (unsigned pos = 0; pos < strlen(path); pos++) {
        if (*(path + pos) == '/') {
            last = pos + 1;
        }
    }

    return path + last;
}

static inline my_bool_t
my_parse_size(const char *str, int64_t *res)
{
    const char *p = str;
    char ch;

    while (*p != '\0') {
        if (*p < '0' || *p > '9') {
            break;
        }
        p++;
    }

    my_str_t tmp = {
        .len = p - str,
        .data = (char *)str,
    };

    if (!my_str_to_int64(&tmp, res)) {
        return MY_FALSE;
    }

    if (*p == '\0') {
        return MY_TRUE;
    }

    ch = *p | 0x20;

    if (ch == 'k') {
        *res = *res * 1024;
        return MY_TRUE;
    }

    if (ch == 'm') {
        *res = *res * 1024 * 1024;
        return MY_TRUE;
    }

    if (ch == 'g') {
        *res = *res * 1024 * 1024 * 1024;
        return MY_TRUE;
    }

    if (ch == 't') {
        *res = *res * 1024 * 1024 * 1024 * 1024;
        return MY_TRUE;
    }

    return MY_FALSE;
}